﻿/*
 * Copyright (c) Martin Kinkelin
 *
 * See the "License.txt" file in the root directory for infos
 * about permitted and prohibited uses of this code.
 */

using System;
using System.Collections.Generic;
using System.Xml;

namespace Jarf
{
	/// <summary>
	/// Associates a source folder to a local or remote destination.
	/// </summary>
	public class BackupTask
	{
		private List<string> _exclusions = new List<string>();



		/// <summary>
		/// Gets the globally unique ID of this backup task.
		/// </summary>
		public string Guid { get; private set; }


		/// <summary>
		/// Gets or sets the path to the source folder to be backed up.
		/// </summary>
		public string Source { get; set; }

		/// <summary>
		/// Gets the list of subfolders/files/patterns to be excluded from backup.
		/// The paths are relative to the source folder.
		/// </summary>
		public List<string> Exclusions
		{
			get { return _exclusions; }
		}


		/// <summary>
		/// Gets or sets the backup destination.
		/// This is either a path to a folder or the URI of a Rsync
		/// server (starting with "rsync://").
		/// </summary>
		public string Destination { get; set; }

		/// <summary>
		/// Gets a value indicating whether the destination is a server.
		/// </summary>
		public bool DestinationIsServer
		{
			get { return Destination.StartsWith("rsync://", StringComparison.CurrentCultureIgnoreCase); }
		}

		/// <summary>
		/// Gets or sets the optional password of the Rsync server.
		/// </summary>
		public string Password { get; set; }


		/// <summary>
		/// Gets or sets a value indicating whether extraneous files and folders
		/// (ones which do not exist in the source folder or are excluded) are
		/// to be deleted from the destination.
		/// </summary>
		public bool DeleteExtraneousFiles { get; set; }

		/// <summary>
		/// Gets or sets a value indicating whether files with a more recent modification
		/// time at the destination are to be overwritten by the ones in the source folder.
		/// </summary>
		public bool OverwriteNewerFiles { get; set; }


		/// <summary>
		/// Gets or sets the date and time of the last successful backup.
		/// </summary>
		public DateTime? LastBackup { get; set; }



		public BackupTask()
		{
			Guid = System.Guid.NewGuid().ToString();
		}


		/// <summary>
		/// Converts the backup task to an XML representation at the specified XML node.
		/// </summary>
		/// <param name="taskNode"></param>
		public void Serialize(XmlNode taskNode)
		{
			if (taskNode == null)
				throw new ArgumentNullException("taskNode");

			XmlDocument document = taskNode.OwnerDocument;

			XmlAttribute attribute = document.CreateAttribute("guid");
			attribute.Value = Guid;
			taskNode.Attributes.Append(attribute);

			XmlElement node = document.CreateElement("source");
			node.InnerText = Source;
			taskNode.AppendChild(node);

			if (Exclusions.Count > 0)
			{
				XmlElement exclusionsNode = document.CreateElement("exclusions");
				taskNode.AppendChild(exclusionsNode);

				foreach (string exclusion in Exclusions)
				{
					node = document.CreateElement("item");
					node.InnerText = exclusion;
					exclusionsNode.AppendChild(node);
				}
			}

			node = document.CreateElement("destination");
			node.InnerText = Destination;
			taskNode.AppendChild(node);

			if (!string.IsNullOrEmpty(Password))
			{
				node = document.CreateElement("password");
				node.InnerText = Password;
				taskNode.AppendChild(node);
			}

			node = document.CreateElement("deleteExtraneousFiles");
			node.InnerText = DeleteExtraneousFiles.ToString();
			taskNode.AppendChild(node);

			node = document.CreateElement("overwriteNewerFiles");
			node.InnerText = OverwriteNewerFiles.ToString();
			taskNode.AppendChild(node);

			if (LastBackup.HasValue)
			{
				node = document.CreateElement("lastBackup");
				node.InnerText = LastBackup.Value.ToUniversalTime().ToString("u");
				taskNode.AppendChild(node);
			}
		}

		/// <summary>
		/// Recreates a backup task from the specified XML node.
		/// </summary>
		/// <param name="taskNode"></param>
		/// <returns></returns>
		public static BackupTask Deserialize(XmlNode taskNode)
		{
			if (taskNode == null)
				throw new ArgumentNullException("taskNode");

			BackupTask task = new BackupTask();

			task.Guid = taskNode.Attributes["guid"].Value;

			task.Source = taskNode.SelectSingleNode("source").InnerText;

			XmlNode node = taskNode.SelectSingleNode("exclusions");
			if (node != null)
			{
				XmlNodeList nodes = node.SelectNodes("item");
				foreach (XmlNode itemNode in nodes)
				{
					task.Exclusions.Add(itemNode.InnerText);
				}
			}

			task.Destination = taskNode.SelectSingleNode("destination").InnerText;

			node = taskNode.SelectSingleNode("password");
			if (node != null)
				task.Password = node.InnerText;

			node = taskNode.SelectSingleNode("deleteExtraneousFiles");
			task.DeleteExtraneousFiles = bool.Parse(node.InnerText);

			node = taskNode.SelectSingleNode("overwriteNewerFiles");
			task.OverwriteNewerFiles = bool.Parse(node.InnerText);

			node = taskNode.SelectSingleNode("lastBackup");
			if (node != null)
			{
				task.LastBackup = DateTime.ParseExact(node.InnerText, "u",
					System.Globalization.CultureInfo.InvariantCulture).ToLocalTime();
			}

			return task;
		}
	}
}
